#include "Server.h"
#include <iostream>
#include "GMSetID.h"
#include "MsgRecv.h"
#include "Timer.h"
#include "AcceptConnectionThread.h"
#include "GMClientDisconnectRequest.h"
#include "GMServerClientPing.h"
#include "GMClientServerPing.h"
#include "GMSetTime.h"
#include "GMKeyChange.h"
#include "GameMessageFactory.h"
#include "Networking.h"

/************************************************************************/
/* SERVER                                                               */
/************************************************************************/
void Server::ShutDown()
{
    //clean threads
    m_acThread->StopAccepting();
    int ret = m_acThread->WaitForFinish(); //max 1 second
    delete m_acThread;
    m_acThread = NULL;

    for(unsigned int i = 0; i < m_msgRecvThreads.size(); ++i)
    {
        m_msgRecvThreads[i]->Halt();
        int t = m_msgRecvThreads[i]->WaitForFinish();
        delete m_msgRecvThreads[i];
    }
    m_msgRecvThreads.clear();
    //clean sockets
    for(unsigned int i = 0; i < m_clientSockets.size(); ++i)
    {
        m_clientSockets[i]->Invalidate();
        delete m_clientSockets[i];
    }
    m_clientSockets.clear();

    //clean messages
    m_messageQueue.clean();
    OUT_STRING("ACThread returned: " << ret);
}
void Server::SetIDs()
{
    OUT_STRING("Setting the Player ID's...");
    //Itterate over all clients and check they are still valid
    int IDcounter = 0;
    for(vSockets::iterator it = m_clientSockets.begin();
        it != m_clientSockets.end(); ++it)
    {
        GMSetID m;
        m.SetID(IDcounter);
        m.SetTimeStamp(m_timeStamp);
        OUT_STRING("Setting PlayerID: " << IDcounter << ",");
        if(!SendGM(m, (*it)))
        {
            OUT_STRING("Setting ID failed...Removing Client: " << IDcounter);
            (*it)->Close();
        }
        IDcounter++;
    }
}
void Server::AddClient( Networking::Socket* client )
{
    if(!client)
    {
        OUT_STRING("Invalid client passed to Server::AddClient");
        return;
    }
    MsgRecv* mr = new MsgRecv;
    m_msgRecvThreads.push_back(mr);
    m_clientSockets.push_back(client);
    mr->SetSocket(client);
    mr->Start();
}
void Server::Update()
{
    TheTimer::Instance()->Update();
    m_timeStamp += TheTimer::Instance()->GetDtMilliSeconds();
    ManageClients();
    m_gameTickCounter++;
    if(m_gameTickCounter > 1000)
    {
        m_gameTickCounter = 0;
        SetIDs();
    }
    bool resul = false;
    GameMessage* gm = NULL;
    gm = m_messageQueue.try_pop(&resul);
    while(resul) //Ask for a msg
    {
        if(gm && gm->GetTimeStamp() < m_timeStamp) //Check its valid
        {
            gm->Execute();
            delete gm;
            //and loop
            gm = m_messageQueue.try_pop(&resul);
        }
        else if (gm) //if not, add it back in at the front (faster than pop
        {
            m_messageQueue.pushFront(gm);
            break;
        }
    }
    m_messageQueue.UpdateTimeStamps(TheTimer::Instance()->GetDtMilliSeconds());
}
void Server::ManageClients()
{
    //Itterate over all clients and check they are still valid
    int IDcounter = 0;
    bool removed = false;
    for(vSockets::iterator it = m_clientSockets.begin();
        it != m_clientSockets.end(); /*     */)
    {
        if(!(*it)->IsOpen())
        {
            OUT_STRING("Socket : " << IDcounter << " is not open, removing");
            (*it)->Invalidate(); // NOT close!
            delete (*it);
            it = m_clientSockets.erase(it);
            removed = true;
        }
        else
        {
            IDcounter++;
            it++;
        }
    }
    if(removed)
        SetIDs(); //They need new ID's now
}
void Server::RemoveClient( int id )
{
    OUT_STRING("Remove Client request! ID: " << id);
    if((unsigned int)id >= m_clientSockets.size())
    {
        OUT_STRING("Invalid ID");
        return;
    }
    //Stop the MSG RCV thread first,
    std::vector<MsgRecv*>::iterator jt = m_msgRecvThreads.begin() + id;
    (*jt)->Halt();
    int t = (*jt)->WaitForFinish();
    delete (*jt);
    m_msgRecvThreads.erase(jt);
    //Now it's safe to delete the socket
    vSockets::iterator it = m_clientSockets.begin() + id;
    (*it)->Close();
    delete (*it);
    m_clientSockets.erase(it);
}
void Server::StartUp( int port )
{
    SDLNet_Init();
    GameMessage::StaticPrepareFactory();
    m_acThread = new AcceptConnectionThread;
    m_acThread->SetPort(port);
    m_acThread->Start();
}
void Server::AddMessageToQueue( GameMessage* gm )
{
    m_messageQueue.push(gm);    
}
bool Server::PingClient( int id )
{
    OUT_STRING("Ping Client request! ID: " << id);
    if((unsigned int)id >= m_clientSockets.size())
    {
        OUT_STRING("Invalid ID");
        return false;
    }
    vSockets::iterator it = m_clientSockets.begin() + id;
    if((*it)->IsOpen())
    {
        //Send the ping here
        GMServerClientPing gm;
        gm.SetTimeStamp(m_timeStamp);
        OUT_STRING("Sending Ping to Client: " << id);
        if(!SendGM(gm,(*it)))
        {
            OUT_STRING("Ping Client failed...removing Client: " << id);
            (*it)->Close();
        }
        return true;
    }
    return false;
}
void Server::UpdateTimes()
{
    int IDCounter = 0;
    for(vSockets::iterator it = m_clientSockets.begin();
        it != m_clientSockets.end(); ++it)
    {
        GMSetTime m;
        m.SetTimeStamp(m_timeStamp);
        if(!SendGM(m,(*it)))
        {
            OUT_STRING("Setting Time failed...Removing Client: " << IDCounter);
            (*it)->Close();
        }
        IDCounter++;
    }
}

/************************************************************************/
/* MESSAGES                                                             */
/************************************************************************/
/* PrepareGameMessage does what needs to be done to EVERY game message
 As we added the timestamp, we needed to make sure to read the time
 stamp in in both RecvGM AND AddGm, this ensures we don't forget to do it
 in one of the functions. We now need to check the return value is not NULL
 */
GameMessage* PrepareGameMessage(Networking::SockMsg& sm)
{
    std::string type;
    if(!sm.ReadString(&type))
    {
        OUT_STRING("SockMsg->ReadString failed, can not deduce GM type. Returning NULL");
        return NULL;
    }
    float ts = 0.0f;
    if(!sm.ReadFloat(&ts))
    {
        OUT_STRING("SockMsg->ReadFloat failed, invalid timestamp. Returning NULL");
        return NULL;
    }
    OUT_STRING("Message Received, type: " << type << " , timeStamp:  "
        << ts);
    GameMessage* m  = TheGameMessageFactory::Instance()->Create(type);
    if(!m)
    {
        OUT_STRING("GameMessage Invalid, type: " << type);
        return NULL;
    }
    m->SetTimeStamp(ts);
    return m;
}
//Adds a message to the queue
bool AddGm( Networking::SockMsg& sm )
{
    //Initial error
    if(sm.Size() <= 0)
        return false;
    while((unsigned int)sm.GetIndex() < sm.Size())
    {
        GameMessage* m = PrepareGameMessage(sm);        
        if(m == NULL)
        {
            OUT_STRING("PrepareGameMessage returned NULL in AddGm, returning false...");
            return false;
        }
        if(!m->ReadFromSckMsg(&sm))
        {
            OUT_STRING("GameMessage->ReadFromSckMsg failed, returning false...");
            delete m;
            return false;
        }
        //At this point add to message Queue
        TheServer::Instance()->AddMessageToQueue(m);
    }

    return true;
}
//posts a msg down the socket
bool SendGM(GameMessage& gm, Networking::Socket* sock)
{
    if(!sock)
    {
        OUT_STRING("Null Socket passes to SendGM, returning false");
        return false;
    }
    Networking::SockMsg sckMsg;
    sckMsg.WriteString(gm.GetTypeName());
    sckMsg.WriteFloat(gm.GetTimeStamp());
    gm.WriteToSckMsg(&sckMsg);
    return sock->Send(sckMsg);
}
//Receives a GM and executes it
bool RecvGM(Networking::SockMsg& sm)
{
    if(sm.Size() <= 0)
        return false;
    GameMessage* m = PrepareGameMessage(sm);
    if(m == NULL)
    {
        OUT_STRING("PrepareGameMessage returned NULL in RecvGM, returning false...");
        return false;
    }
    if(!m->ReadFromSckMsg(&sm))
    {
        OUT_STRING("GameMessage->ReadFromSckMsg failed, returning false...");
        delete m;
        return false;
    }
    if(!m->Execute())
    {
        OUT_STRING("GameMessage->Execute failed, returning false...");
        delete m;
        return false;
    }
    delete m;
    return true;
}
